#include <string>
#include "base/core/mem_tracker.h"
#include "base/core/os/file.h"
#include "base/core/log.h"
#include "base/core/os/time.h"
#include "base/core/os/string.h"
#include "base/core/auto_lock.h"

TrackData::TrackData(uint32_t line_no,
                     const char* file_name,
                     const char* func_name) 
{
	line_no_   = line_no;
	file_name_ = std::string(file_name);
	func_name_ = std::string(func_name);
}

MemTracker::MemTracker() {}

MemTracker::~MemTracker() {}

bool MemTracker::TrackMalloc(void* ptr,
                             const char* file_path,
                             const char* func_name,
                             uint32_t line_no) 
{
	MMO_ASSERT(ptr!= NULL);
	if (ptr == NULL) 
	{
		return false;
	}
	std::string file_name = GetFileNameFromPath(file_path);
	AutoLock<TMutexLock> temp_lock(&mutex_lock_);

	if (ptr_track_map_.find(ptr) != ptr_track_map_.end()) 
	{
		MMO_ASSERT(false);
		return false;
	}
	ptr_track_map_.insert(
	PtrTrackMap::value_type(ptr, TrackData(line_no, file_name.c_str(), func_name)));
	return true;
}

void MemTracker::TrackFree(void* ptr) 
{
	MMO_ASSERT(ptr != NULL);
	if (ptr == NULL) 
	{
		return;
	}
	AutoLock<TMutexLock> temp_lock(&mutex_lock_);
	size_t num = ptr_track_map_.erase(ptr);
	if (num != 1) 
	{
		OutputFmtToConsole("error, num:%lu\n", num);
	}
	MMO_ASSERT(num == 1);
}

void MemTracker::PrintMemLink(const char* output_filename) 
{
	AutoLock<TMutexLock> temp_lock(&mutex_lock_);
	File* file = NULL;
	if (output_filename) 
	{
		file = FileOpen(output_filename, "at+");
	}
	if (file) 
	{
		char DateTiemBuf[64];
		Time::Now().LocalDateTime(DateTiemBuf, sizeof(DateTiemBuf));
		std::string contentHead = std::string("---------------------")
								+ DateTiemBuf
								+ std::string("---------------------\n");
		file->Write(contentHead.c_str(), (uint32_t)contentHead.length());
	}
	if (ptr_track_map_.empty()) 
	{
		std::string no_link_mem("OK, No Memory Link\n");
		LogInfoPrint(no_link_mem.c_str());

		if (file) 
		{
			file->Write(no_link_mem.c_str(), (uint32_t)no_link_mem.length());
		}
	} 
	else 
	{
		char formatBuf[512];
		PtrTrackMap::iterator iter = ptr_track_map_.begin();
		PtrTrackMap::iterator iterEnd = ptr_track_map_.end();
		for (; iter != iterEnd; ++iter) 
		{
			TrackData& trackData = iter->second;
			snprintf(formatBuf, sizeof(formatBuf),
			"%s(%u) %-32s link memory\n",
			trackData.file_name_.c_str(),
			trackData.line_no_,
			trackData.func_name_.c_str() );
			LogErrFmtPrint(formatBuf);
			if (file) 
			{
				file->Write(formatBuf, (uint32_t)strlen(formatBuf));
			}
		}
	}

	if (file) 
	{
		FileClose(file);
	}
}

// get just filename from path
std::string MemTracker::GetFileNameFromPath(const char* file_path) 
{
	if (file_path == NULL)
	{
		return std::string("");
	}
	std::string  fileName(file_path);
	std::string::size_type findPos = fileName.rfind(MMO_PATH_SEPRATOR);
	if (findPos == fileName.npos) 
	{
		return fileName;
	}
	else 
	{
		std::string::size_type startPos   = findPos + 1;
		std::string::size_type nameLength = fileName.length() - startPos;
		return fileName.substr(startPos, nameLength);
	}
}